package lookandfeel.Dmetal;

//import javax.swing.plaf.basic.*;

/**
 * <p>Title: </p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2004</p>
 * <p>Company: </p>
 * @author not attributable
 * @version 1.0
 */

/*public class MetalPopupMenuUI extends BasicPopupMenuUI {
 public MetalPopupMenuUI() {
 super();
 }

 protected void installListeners() {
 System.out.println("BasicPop");
 }

 }
 */

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.KeyboardFocusManager;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import java.awt.event.WindowListener;
import java.util.Vector;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.BoxLayout;
import javax.swing.InputMap;
import javax.swing.JApplet;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JRootPane;
import javax.swing.LookAndFeel;
import javax.swing.MenuElement;
import javax.swing.MenuSelectionManager;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.plaf.ActionMapUIResource;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.PopupMenuUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicLookAndFeel;
import javax.swing.plaf.basic.BasicMenuItemUI;
import javax.swing.plaf.basic.DefaultMenuLayout;

/**
 * A Windows L&F implementation of PopupMenuUI. This implementation is a
 * "combined" view/controller.
 * 
 * @version 1.101 01/31/03
 * @author Georges Saab
 * @author David Karlton
 * @author Arnaud Weber
 */
@SuppressWarnings( "unchecked" )
public class DMetalPopupMenuUI extends PopupMenuUI
{
	protected JPopupMenu popupMenu = null;

	static boolean menuKeyboardHelperInstalled = false;

	static MenuKeyboardHelper menuKeyboardHelper = null;

	private static boolean checkInvokerEqual(MenuElement present,
			MenuElement last)
	{
		Component invokerPresent = present.getComponent();
		Component invokerLast = last.getComponent();

		if (invokerPresent instanceof JPopupMenu)
			invokerPresent = ( (JPopupMenu) invokerPresent ).getInvoker();
		if (invokerLast instanceof JPopupMenu)
			invokerLast = ( (JPopupMenu) invokerLast ).getInvoker();
		return ( invokerPresent == invokerLast );
	}

	public static ComponentUI createUI(JComponent x)
	{
		return new DMetalPopupMenuUI();
	}

	@Override
	public void installUI(JComponent c)
	{
		popupMenu = (JPopupMenu) c;

		installDefaults();
		installListeners();
		installKeyboardActions();
	}

	public void installDefaults()
	{
		if (( popupMenu.getLayout() == null )
				|| ( popupMenu.getLayout() instanceof UIResource ))
			popupMenu.setLayout(new DefaultMenuLayout(popupMenu,
					BoxLayout.Y_AXIS));
		popupMenu.setOpaque(true);
		LookAndFeel.installBorder(popupMenu, "PopupMenu.border");
		LookAndFeel.installColorsAndFont(popupMenu, "PopupMenu.background",
				"PopupMenu.foreground", "PopupMenu.font");
	}

	protected void installListeners()
	{
		if (mouseGrabber == null) mouseGrabber = new MouseGrabber();
		if (metalPopupMenuListener == null)
			metalPopupMenuListener = createPopupMenuListener();
		popupMenu.addPopupMenuListener(metalPopupMenuListener);

		if (!menuKeyboardHelperInstalled)
			if (menuKeyboardHelper == null)
				menuKeyboardHelper = new MenuKeyboardHelper();
	}

	protected void installKeyboardActions()
	{
	}

	static InputMap getInputMap(JPopupMenu popup, JComponent c)
	{
		InputMap windowInputMap = null;
		Object[] bindings = (Object[]) UIManager
				.get("PopupMenu.selectedWindowInputMapBindings");
		if (bindings != null)
		{
			windowInputMap = LookAndFeel.makeComponentInputMap(c, bindings);
			if (!popup.getComponentOrientation().isLeftToRight())
			{
				Object[] km = (Object[]) UIManager
						.get("PopupMenu.selectedWindowInputMapBindings.RightToLeft");
				if (km != null)
				{
					InputMap rightToLeftInputMap = LookAndFeel
							.makeComponentInputMap(c, km);
					rightToLeftInputMap.setParent(windowInputMap);
					windowInputMap = rightToLeftInputMap;
				}
			}
		}
		return windowInputMap;
	}

	static ActionMap getActionMap()
	{
		ActionMap map = (ActionMap) UIManager.get("PopupMenu.actionMap");
		if (map == null)
		{
			map = createActionMap();
			if (map != null)
				UIManager.getLookAndFeelDefaults().put("PopupMenu.actionMap",
						map);
		}
		return map;
	}

	static ActionMap createActionMap()
	{
		ActionMap map = new ActionMapUIResource();
		// Set the ActionMap's parent to the Auditory Feedback Action Map
		MetalLookAndFeel lf = (MetalLookAndFeel) UIManager.getLookAndFeel();
		ActionMap audioMap = lf.getAudioActionMap();
		map.setParent(audioMap);

		map.put("cancel", new CancelAction());
		map.put("selectNext", new SelectNextItemAction(
				SelectNextItemAction.FORWARD));
		map.put("selectPrevious", new SelectNextItemAction(
				SelectNextItemAction.BACKWARD));
		map.put("selectParent", new SelectParentChildAction(
				SelectParentChildAction.PARENT));
		map.put("selectChild", new SelectParentChildAction(
				SelectParentChildAction.CHILD));
		map.put("return", new ReturnAction());

		// return the created map
		return map;
	}

	@Override
	public void uninstallUI(JComponent c)
	{
		uninstallDefaults();
		uninstallListeners();
		uninstallKeyboardActions();

		popupMenu = null;
	}

	protected void uninstallDefaults()
	{
		LookAndFeel.uninstallBorder(popupMenu);
	}

	protected void uninstallListeners()
	{
		if (metalPopupMenuListener != null)
			popupMenu.removePopupMenuListener(metalPopupMenuListener);
	}

	protected void uninstallKeyboardActions()
	{
		SwingUtilities.replaceUIActionMap(popupMenu, null);
		SwingUtilities.replaceUIInputMap(popupMenu,
				JComponent.WHEN_IN_FOCUSED_WINDOW, null);
	}

	@Override
	public Dimension getMinimumSize(JComponent c)
	{
		return getPreferredSize(c);
	}

	@Override
	public Dimension getPreferredSize(JComponent c)
	{
		return null;
	}

	@Override
	public Dimension getMaximumSize(JComponent c)
	{
		return getPreferredSize(c);
	}

	// /////////////////////////////////////////////////////////////////////////////
	// // Grab Code
	// /////////////////////////////////////////////////////////////////////////////
	@SuppressWarnings("unused")
	private static Window getWindow(Component c)
	{
		Component w = c;

		while (!( w instanceof Window ) && ( w != null ))
			w = w.getParent();
		return (Window) w;
	}

	private static MenuElement getFirstPopup()
	{
		MenuSelectionManager msm = MenuSelectionManager.defaultManager();
		MenuElement[] p = msm.getSelectedPath();
		MenuElement me = null;

		for (int i = 0; ( me == null ) && ( i < p.length ); i++)
			if (p[i] instanceof JPopupMenu) me = p[i];

		return me;
	}

	private static JPopupMenu getLastPopup()
	{
		MenuSelectionManager msm = MenuSelectionManager.defaultManager();
		MenuElement[] p = msm.getSelectedPath();
		JPopupMenu popup = null;

		for (int i = p.length - 1; ( popup == null ) && ( i >= 0 ); i--)
			if (p[i] instanceof JPopupMenu) popup = (JPopupMenu) p[i];
		return popup;
	}

	private transient static MouseGrabber mouseGrabber = null;

	private static class MouseGrabber implements MouseListener,
			MouseMotionListener, MouseWheelListener, WindowListener,
			WindowFocusListener, ComponentListener, ChangeListener
	{
		Vector<Object> grabbed = new Vector<Object>();

		MenuElement lastGrabbed = null;

		boolean lastGrabbedMenuBarChild = false;

		public MouseGrabber()
		{
			// MenuSelectionManager msm = MenuSelectionManager.defaultManager();
			// msm.addChangeListener(this);
		}

		@SuppressWarnings("unused")
		private void requestRemoveGrab()
		{
			ungrabContainers();
		}

		void cancelPopupMenu()
		{
			/*
			 * MenuSelectionManager msm = MenuSelectionManager.defaultManager();
			 * MenuElement[] p = msm.getSelectedPath();
			 * 
			 * System.out.println("MetalPoupMenuUI: cancelPopupMenu()");
			 * 
			 * JPopupMenu firstPopup = (JPopupMenu) getFirstPopup(); if
			 * (lastGrabbed == firstPopup) { // 4234793: This action should call
			 * firePopupMenuCanceled but it's // a protected method. The real
			 * solution could be to make // firePopupMenuCanceled public and
			 * call it directly. List popups = getPopups(); Iterator iter =
			 * popups.iterator(); while (iter.hasNext()) { JPopupMenu popup =
			 * (JPopupMenu) iter.next();
			 * popup.putClientProperty("JPopupMenu.firePopupMenuCanceled",
			 * Boolean.TRUE); }
			 * MenuSelectionManager.defaultManager().clearSelectedPath();
			 * ungrabContainers(); } else { // The cancel has cause another menu
			 * selection lastGrabbed = firstPopup; if (p[0] instanceof JMenuBar) {
			 * lastGrabbedMenuBarChild = true; } else { lastGrabbedMenuBarChild =
			 * false; } }
			 */
			// System.out.println("MetalPopupMenuUI: cancelled");
		}

		MenuElement[] lastPathSelected = new MenuElement[0];

		public void stateChanged(ChangeEvent e)
		{
			/*
			 * MenuSelectionManager msm = MenuSelectionManager.defaultManager();
			 * MenuElement[] p = msm.getSelectedPath();
			 * 
			 * if (lastPathSelected.length != 0 && p.length != 0) { if
			 * (!checkInvokerEqual(p[0], lastPathSelected[0])) {
			 * requestRemoveGrab(); lastPathSelected = new MenuElement[0]; } }
			 * 
			 * if (lastPathSelected.length == 0 && p.length != 0) { // A grab
			 * needs to be added Component invoker = p[0].getComponent(); if
			 * (invoker instanceof JPopupMenu) invoker = ( (JPopupMenu)
			 * invoker).getInvoker(); requestAddGrab(invoker); }
			 * 
			 * if (lastPathSelected.length != 0 && p.length == 0) { // The grab
			 * should be removed requestRemoveGrab(); } // Switch between
			 * menubar children if (p != null && p.length > 2 && (p[0]
			 * instanceof JMenuBar && lastGrabbedMenuBarChild == true)) {
			 * 
			 * if (! (lastGrabbed == getFirstPopup())) { lastGrabbed =
			 * getFirstPopup();
			 * 
			 * if (p[0] instanceof JMenuBar) { lastGrabbedMenuBarChild = true; }
			 * else { lastGrabbedMenuBarChild = false; } } } // Remember the
			 * last path selected lastPathSelected = p;
			 */
		}

		void grabContainer(Container c, Component excluded)
		{
			if (c == excluded) return;

			MenuSelectionManager msm = MenuSelectionManager.defaultManager();
			MenuElement[] p = msm.getSelectedPath();
			lastGrabbed = getFirstPopup();
			if (p[0] instanceof JMenuBar)
				lastGrabbedMenuBarChild = true;
			else lastGrabbedMenuBarChild = false;

			Component ignore = null;
			if (c instanceof java.awt.Window)
			{
				( (java.awt.Window) c ).addWindowListener(this);
				( (java.awt.Window) c ).addWindowFocusListener(this);
				( (java.awt.Window) c ).addComponentListener(this);
				grabbed.addElement(c);
			}
			else if (c instanceof JRootPane)
			{
				// Only add listeners to the glass pane if it listeners,
				// otherwise if the glass pane is visible no mouse move
				// events will come to us.
				ignore = ( (JRootPane) c ).getGlassPane();
				if (( ignore.getMouseListeners().length > 0 )
						|| ( ignore.getMouseMotionListeners().length > 0 )
						|| ( ignore.getMouseWheelListeners().length > 0 ))
					ignore = null;
			}
			synchronized (c.getTreeLock())
			{
				int ncomponents = c.getComponentCount();
				Component[] component = c.getComponents();
				for (int i = 0; i < ncomponents; i++)
				{
					Component comp = component[i];
					if (!comp.isVisible()) continue;
					if (comp != ignore)
					{
						comp.addMouseListener(this);
						comp.addMouseMotionListener(this);
						comp.addMouseWheelListener(this);
						grabbed.addElement(comp);
					}
					if (comp instanceof Container)
					{
						Container cont = (Container) comp;
						grabContainer(cont, excluded);
					}
				}
			}

		}

		void ungrabContainers()
		{
			int i, c;
			Component cpn;
			for (i = 0, c = grabbed.size(); i < c; i++)
			{
				cpn = (Component) grabbed.elementAt(i);
				if (cpn instanceof java.awt.Window)
				{
					( (java.awt.Window) cpn ).removeWindowListener(this);
					( (java.awt.Window) cpn ).removeWindowFocusListener(this);
					( (java.awt.Window) cpn ).removeComponentListener(this);
				}
				else
				{
					cpn.removeMouseListener(this);
					cpn.removeMouseMotionListener(this);
					cpn.removeMouseWheelListener(this);
				}
			}
			grabbed = new Vector();
			lastGrabbed = null;
			lastGrabbedMenuBarChild = false;
		}

		public void mousePressed(MouseEvent e)
		{
			cancelPopupMenu();
			/*
			 * if(msm.isComponentPartOfCurrentMenu(popupMenu) &&
			 * msm.isComponentPartOfCurrentMenu(c)) { return; } else {
			 * cancelPopupMenu(); }
			 */
		}

		public void mouseReleased(MouseEvent e)
		{
			// MenuSelectionManager.defaultManager().processMouseEvent(e);
		}

		public void mouseEntered(MouseEvent e)
		{
			// MenuSelectionManager.defaultManager().processMouseEvent(e);
		}

		public void mouseExited(MouseEvent e)
		{
			// MenuSelectionManager.defaultManager().processMouseEvent(e);
		}

		public void mouseMoved(MouseEvent e)
		{
			// MenuSelectionManager.defaultManager().processMouseEvent(e);
		}

		public void mouseDragged(MouseEvent e)
		{
			// MenuSelectionManager.defaultManager().processMouseEvent(e);
		}

		public void mouseClicked(MouseEvent e)
		{
		}

		public void mouseWheelMoved(MouseWheelEvent e)
		{
			// cancelPopupMenu();
		}

		public void componentResized(ComponentEvent e)
		{
			// cancelPopupMenu();
		}

		public void componentMoved(ComponentEvent e)
		{
			// cancelPopupMenu();
		}

		public void componentShown(ComponentEvent e)
		{
			// cancelPopupMenu();
		}

		public void componentHidden(ComponentEvent e)
		{
			// cancelPopupMenu();
		}

		public void windowOpened(WindowEvent e)
		{
		}

		public void windowClosing(WindowEvent e)
		{
			// cancelPopupMenu();
		}

		public void windowClosed(WindowEvent e)
		{
			// cancelPopupMenu();
		}

		public void windowIconified(WindowEvent e)
		{
			// cancelPopupMenu();
		}

		public void windowDeiconified(WindowEvent e)
		{
		}

		public void windowActivated(WindowEvent e)
		{
		}

		public void windowDeactivated(WindowEvent e)
		{
		}

		public void windowLostFocus(WindowEvent e)
		{
		}

		public void windowGainedFocus(WindowEvent e)
		{
		}
	}

	@Override
	public boolean isPopupTrigger(MouseEvent e)
	{
		return ( ( e.getID() == MouseEvent.MOUSE_RELEASED ) && ( ( e
				.getModifiers() & InputEvent.BUTTON3_MASK ) != 0 ) );
	}

	// for auditory feedback
	private transient PopupMenuListener metalPopupMenuListener = null;

	// for auditory feedback
	private PopupMenuListener createPopupMenuListener()
	{
		return new metalPopupMenuListener();
	}

	/**
	 * This Listener fires the Action that provides the correct auditory
	 * feedback.
	 * 
	 * @since 1.4
	 */
	private class metalPopupMenuListener implements PopupMenuListener
	{
		public void popupMenuCanceled(PopupMenuEvent e)
		{
		}

		public void popupMenuWillBecomeInvisible(PopupMenuEvent e)
		{
		}

		public void popupMenuWillBecomeVisible(PopupMenuEvent e)
		{
			// Fire the Action that renders the auditory cue.
			JPopupMenu pm = (JPopupMenu) e.getSource();
			ActionMap map = pm.getActionMap();
			if (map != null)
			{
				Action audioAction = map.get("PopupMenu.popupSound");
				if (audioAction != null)
				{
					// pass off firing the Action to a utility method
					MetalLookAndFeel lf = (MetalLookAndFeel) UIManager
							.getLookAndFeel();
					lf.playSound(audioAction);
				}
			}
		}
	}

	private static class CancelAction extends AbstractAction
	{
		/**
		 * 
		 */
		private static final long serialVersionUID = 4785822922500639663L;

		public void actionPerformed(ActionEvent e)
		{

			// 4234793: This action should call JPopupMenu.firePopupMenuCanceled
			// but it's
			// a protected method. The real solution could be to make
			// firePopupMenuCanceled public and call it directly.
			JPopupMenu lastPopup = getLastPopup();
			if (lastPopup != null)
				lastPopup.putClientProperty("JPopupMenu.firePopupMenuCanceled",
						Boolean.TRUE);

			MenuElement path[] = MenuSelectionManager.defaultManager()
					.getSelectedPath();
			if (path.length > 4)
			{ /*
				 * PENDING(arnaud) Change this to 2 when a mouse grabber is
				 * available for MenuBar
				 */
				MenuElement newPath[] = new MenuElement[path.length - 2];
				System.arraycopy(path, 0, newPath, 0, path.length - 2);
				MenuSelectionManager.defaultManager().setSelectedPath(newPath);
			}
			else MenuSelectionManager.defaultManager().clearSelectedPath();
		}
	}

	private static class ReturnAction extends AbstractAction
	{
		/**
		 * 
		 */
		private static final long serialVersionUID = -6311333616651968965L;

		public void actionPerformed(ActionEvent e)
		{
			KeyboardFocusManager fmgr = KeyboardFocusManager
					.getCurrentKeyboardFocusManager();
			Component focusOwner = fmgr.getFocusOwner();
			if (( focusOwner != null ) && !( focusOwner instanceof JRootPane ))
				return;

			MenuSelectionManager msm = MenuSelectionManager.defaultManager();
			MenuElement path[] = msm.getSelectedPath();
			MenuElement lastElement;
			if (path.length > 0)
			{
				lastElement = path[path.length - 1];
				if (lastElement instanceof JMenu)
				{
					MenuElement newPath[] = new MenuElement[path.length + 1];
					System.arraycopy(path, 0, newPath, 0, path.length);
					newPath[path.length] = ( (JMenu) lastElement )
							.getPopupMenu();
					msm.setSelectedPath(newPath);
				}
				else if (lastElement instanceof JMenuItem)
				{
					JMenuItem mi = (JMenuItem) lastElement;

					if (mi.getUI() instanceof BasicMenuItemUI)
					{
						// Se comenta por razones de acceso. Aparentemente no
						// habra problemas ya que esta clase solo se usara para 
						//el popup del JComboBox
						// ( (BasicMenuItemUI) mi.getUI()).doClick(msm);
					}
					else
					{
						msm.clearSelectedPath();
						mi.doClick(0);
					}
				}
			}
		}
	}

	private static MenuElement nextEnabledChild(MenuElement e[], int fromIndex,
			int toIndex)
	{
		for (int i = fromIndex; i <= toIndex; i++)
			if (e[i] != null)
			{
				Component comp = e[i].getComponent();
				if (( comp != null ) && comp.isEnabled()) return e[i];
			}
		return null;
	}

	private static MenuElement previousEnabledChild(MenuElement e[],
			int fromIndex, int toIndex)
	{
		for (int i = fromIndex; i >= toIndex; i--)
			if (e[i] != null)
			{
				Component comp = e[i].getComponent();
				if (( comp != null ) && comp.isEnabled()) return e[i];
			}
		return null;
	}

	private static MenuElement findEnabledChild(MenuElement e[], int fromIndex,
			boolean forward)
	{
		MenuElement result = null;
		if (forward)
		{
			result = nextEnabledChild(e, fromIndex + 1, e.length - 1);
			if (result == null) result = nextEnabledChild(e, 0, fromIndex - 1);
		}
		else
		{
			result = previousEnabledChild(e, fromIndex - 1, 0);
			if (result == null)
				result = previousEnabledChild(e, e.length - 1, fromIndex + 1);
		}
		return result;
	}

	private static MenuElement findEnabledChild(MenuElement e[],
			MenuElement elem, boolean forward)
	{
		for (int i = 0; i < e.length; i++)
			if (e[i] == elem) return findEnabledChild(e, i, forward);
		return null;
	}

	private static class SelectNextItemAction extends AbstractAction
	{
		/**
		 * 
		 */
		private static final long serialVersionUID = -3115547453470044413L;

		static boolean FORWARD = true;

		static boolean BACKWARD = false;

		boolean direction;

		SelectNextItemAction( boolean dir )
		{
			direction = dir;
		}

		public void actionPerformed(ActionEvent e)
		{
			MenuSelectionManager msm = MenuSelectionManager.defaultManager();
			MenuElement path[] = msm.getSelectedPath();
			if (path.length < 2) return;
			int len = path.length;

			if (( path[0] instanceof JMenuBar ) && ( path[1] instanceof JMenu )
					&& ( len == 2 ))
			{

				// a toplevel menu is selected, but its popup not shown.
				// Show the popup and select the first item
				JPopupMenu popup = ( (JMenu) path[1] ).getPopupMenu();
				MenuElement next = findEnabledChild(popup.getSubElements(), -1,
						FORWARD);
				MenuElement[] newPath;

				if (next != null)
				{
					// an enabled item found -- include it in newPath
					newPath = new MenuElement[4];
					newPath[3] = next;
				}
				else // menu has no enabled items -- still must show the
				// popup
				newPath = new MenuElement[3];
				System.arraycopy(path, 0, newPath, 0, 2);
				newPath[2] = popup;
				msm.setSelectedPath(newPath);

			}
			else if (( path[len - 1] instanceof JPopupMenu )
					&& ( path[len - 2] instanceof JMenu ))
			{

				// a menu (not necessarily toplevel) is open and its popup
				// shown. Select the appropriate menu item
				JMenu menu = (JMenu) path[len - 2];
				JPopupMenu popup = menu.getPopupMenu();
				MenuElement next = findEnabledChild(popup.getSubElements(), -1,
						direction);

				if (next != null)
				{
					MenuElement[] newPath = new MenuElement[len + 1];
					System.arraycopy(path, 0, newPath, 0, len);
					newPath[len] = next;
					msm.setSelectedPath(newPath);
				}
				else // all items in the popup are disabled.
				// We're going to find the parent popup menu and select
				// its next item. If there's no parent popup menu (i.e.
				// current menu is toplevel), do nothing
				if (( len > 2 ) && ( path[len - 3] instanceof JPopupMenu ))
				{
					popup = ( (JPopupMenu) path[len - 3] );
					next = findEnabledChild(popup.getSubElements(), menu,
							direction);

					if (( next != null ) && ( next != menu ))
					{
						MenuElement[] newPath = new MenuElement[len - 1];
						System.arraycopy(path, 0, newPath, 0, len - 2);
						newPath[len - 2] = next;
						msm.setSelectedPath(newPath);
					}
				}

			}
			else
			{
				// just select the next item, no path expansion needed
				MenuElement subs[] = path[len - 2].getSubElements();
				MenuElement nextChild = findEnabledChild(subs, path[len - 1],
						direction);
				if (nextChild == null)
					nextChild = findEnabledChild(subs, -1, direction);
				if (nextChild != null)
				{
					path[len - 1] = nextChild;
					msm.setSelectedPath(path);
				}
			}
		}
	}

	private static class SelectParentChildAction extends AbstractAction
	{
		/**
		 * 
		 */
		private static final long serialVersionUID = 3961683480443963157L;

		static boolean PARENT = false;

		static boolean CHILD = true;

		boolean direction;

		SelectParentChildAction( boolean dir )
		{
			direction = dir;
		}

		public void actionPerformed(ActionEvent e)
		{
			MenuSelectionManager msm = MenuSelectionManager.defaultManager();
			MenuElement path[] = msm.getSelectedPath();
			int len = path.length;

			if (direction == PARENT)
			{
				// selecting parent
				int popupIndex = len - 1;

				if (( len > 2 )
						&&
						// check if we have an open submenu. A submenu item may
						// or
						// may not be selected, so submenu popup can be either
						// the
						// last or next to the last item.
						( ( path[popupIndex] instanceof JPopupMenu ) || ( path[--popupIndex] instanceof JPopupMenu ) )
						&& !( (JMenu) path[popupIndex - 1] ).isTopLevelMenu())
				{

					// we have a submenu, just close it
					MenuElement newPath[] = new MenuElement[popupIndex];
					System.arraycopy(path, 0, newPath, 0, popupIndex);
					msm.setSelectedPath(newPath);
					return;
				}
			}
			else // selecting child
			if (( len > 0 ) && ( path[len - 1] instanceof JMenu )
					&& !( (JMenu) path[len - 1] ).isTopLevelMenu())
			{

				// we have a submenu, open it
				JMenu menu = (JMenu) path[len - 1];
				JPopupMenu popup = menu.getPopupMenu();
				MenuElement[] subs = popup.getSubElements();
				MenuElement item = findEnabledChild(subs, -1, true);
				MenuElement[] newPath;

				if (item == null)
					newPath = new MenuElement[len + 1];
				else
				{
					newPath = new MenuElement[len + 2];
					newPath[len + 1] = item;
				}
				System.arraycopy(path, 0, newPath, 0, len);
				newPath[len] = popup;
				msm.setSelectedPath(newPath);
				return;
			}

			// check if we have a toplevel menu selected.
			// If this is the case, we select another toplevel menu
			if (( len > 1 ) && ( path[0] instanceof JMenuBar ))
			{
				MenuElement currentMenu = path[1];
				MenuElement nextMenu = findEnabledChild(path[0]
						.getSubElements(), currentMenu, direction);

				if (( nextMenu != null ) && ( nextMenu != currentMenu ))
				{
					MenuElement newSelection[];
					if (len == 2)
					{
						// menu is selected but its popup not shown
						newSelection = new MenuElement[2];
						newSelection[0] = path[0];
						newSelection[1] = nextMenu;
					}
					else
					{
						// menu is selected and its popup is shown
						newSelection = new MenuElement[3];
						newSelection[0] = path[0];
						newSelection[1] = nextMenu;
						newSelection[2] = ( (JMenu) nextMenu ).getPopupMenu();
					}
					msm.setSelectedPath(newSelection);
				}
			}
		}
	}

	/**
	 * This helper is added to MenuSelectionManager as a ChangeListener to
	 * listen to menu selection changes. When a menu is activated, it passes
	 * focus to its parent JRootPane, and installs an ActionMap/InputMap pair on
	 * that JRootPane. Those maps are necessary in order for menu navigation to
	 * work. When menu is being deactivated, it restores focus to the component
	 * that has had it before menu activation, and uninstalls the maps. This
	 * helper is also installed as a KeyListener on root pane when menu is
	 * active. It forwards key events to MenuSelectionManager for mnemonic keys
	 * handling.
	 */
	private static class MenuKeyboardHelper implements ChangeListener,
			KeyListener
	{

		private Component lastFocused = null;

		private MenuElement[] lastPathSelected = new MenuElement[0];

		private JPopupMenu lastPopup;

		private JRootPane invokerRootPane;

		private ActionMap menuActionMap = getActionMap();

		private InputMap menuInputMap;

		private boolean focusTraversalKeysEnabled;

		/*
		 * Fix for 4213634 If this is false, KEY_TYPED and KEY_RELEASED events
		 * are NOT processed. This is needed to avoid activating a menuitem when
		 * the menu and menuitem share the same mnemonic.
		 */
		private boolean receivedKeyPressed = false;

		void removeItems()
		{
			if (lastFocused != null)
			{
				if (!lastFocused.requestFocusInWindow())
				{
					// Workarounr for 4810575.
					// If lastFocused is not in currently focused window
					// requestFocusInWindow will fail. In this case we must
					// request focus by requestFocus() if it was not
					// transferred from our popup.
					Window cfw = KeyboardFocusManager
							.getCurrentKeyboardFocusManager()
							.getFocusedWindow();
					if (( cfw != null )
							&& "###focusableSwingPopup###"
									.equals(cfw.getName()))
						lastFocused.requestFocus();

				}
				lastFocused = null;
			}
			if (invokerRootPane != null)
			{
				invokerRootPane.removeKeyListener(menuKeyboardHelper);
				invokerRootPane
						.setFocusTraversalKeysEnabled(focusTraversalKeysEnabled);
				removeUIInputMap(invokerRootPane, menuInputMap);
				removeUIActionMap(invokerRootPane, menuActionMap);
				invokerRootPane = null;
			}
			receivedKeyPressed = false;
		}

		private FocusListener rootPaneFocusListener = new FocusAdapter()
		{
			@Override
			public void focusGained(FocusEvent ev)
			{
				Component opposite = ev.getOppositeComponent();
				if (opposite != null) lastFocused = opposite;
				ev.getComponent().removeFocusListener(this);
			}
		};

		/**
		 * Return the last JPopupMenu in <code>path</code>, or
		 * <code>null</code> if none found
		 */
		JPopupMenu getActivePopup(MenuElement[] path)
		{
			for (int i = path.length - 1; i >= 0; i--)
			{
				MenuElement elem = path[i];
				if (elem instanceof JPopupMenu) return (JPopupMenu) elem;
			}
			return null;
		}

		void addUIInputMap(JComponent c, InputMap map)
		{
			InputMap lastNonUI = null;
			InputMap parent = c.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);

			while (( parent != null ) && !( parent instanceof UIResource ))
			{
				lastNonUI = parent;
				parent = parent.getParent();
			}

			if (lastNonUI == null)
				c.setInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW, map);
			else lastNonUI.setParent(map);
			map.setParent(parent);
		}

		void addUIActionMap(JComponent c, ActionMap map)
		{
			ActionMap lastNonUI = null;
			ActionMap parent = c.getActionMap();

			while (( parent != null ) && !( parent instanceof UIResource ))
			{
				lastNonUI = parent;
				parent = parent.getParent();
			}

			if (lastNonUI == null)
				c.setActionMap(map);
			else lastNonUI.setParent(map);
			map.setParent(parent);
		}

		void removeUIInputMap(JComponent c, InputMap map)
		{
			InputMap im = null;
			InputMap parent = c.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);

			while (parent != null)
			{
				if (parent == map)
				{
					if (im == null)
						c.setInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW, map
								.getParent());
					else im.setParent(map.getParent());
					break;
				}
				im = parent;
				parent = parent.getParent();
			}
		}

		void removeUIActionMap(JComponent c, ActionMap map)
		{
			ActionMap im = null;
			ActionMap parent = c.getActionMap();

			while (parent != null)
			{
				if (parent == map)
				{
					if (im == null)
						c.setActionMap(map.getParent());
					else im.setParent(map.getParent());
					break;
				}
				im = parent;
				parent = parent.getParent();
			}
		}

		public void stateChanged(ChangeEvent ev)
		{
			if (!( UIManager.getLookAndFeel() instanceof BasicLookAndFeel ))
			{
				MenuSelectionManager msm = MenuSelectionManager
						.defaultManager();
				msm.removeChangeListener(this);
				menuKeyboardHelperInstalled = false;
				return;
			}
			MenuSelectionManager msm = (MenuSelectionManager) ev.getSource();
			MenuElement[] p = msm.getSelectedPath();
			JPopupMenu popup = getActivePopup(p);
			if (( popup != null ) && !popup.isFocusable()) // Do nothing for
				// non-focusable
				// popups
				return;

			if (( lastPathSelected.length != 0 ) && ( p.length != 0 ))
				if (!checkInvokerEqual(p[0], lastPathSelected[0]))
				{
					removeItems();
					lastPathSelected = new MenuElement[0];
				}

			if (( lastPathSelected.length == 0 ) && ( p.length > 0 ))
			{
				// menu posted
				JComponent invoker;

				if (popup == null)
				{
					if (( p.length == 2 ) && ( p[0] instanceof JMenuBar )
							&& ( p[1] instanceof JMenu ))
					{
						// a menu has been selected but not open
						invoker = (JComponent) p[1];
						popup = ( (JMenu) invoker ).getPopupMenu();
					}
					else return;
				}
				else
				{
					Component c = popup.getInvoker();
					if (c instanceof JFrame)
						invoker = ( (JFrame) c ).getRootPane();
					else if (c instanceof JApplet)
						invoker = ( (JApplet) c ).getRootPane();
					else
					{
						while (!( c instanceof JComponent ))
						{
							if (c == null) return;
							c = c.getParent();
						}
						invoker = (JComponent) c;
					}
				}

				// remember current focus owner
				lastFocused = KeyboardFocusManager
						.getCurrentKeyboardFocusManager().getFocusOwner();

				// request focus on root pane and install keybindings
				// used for menu navigation
				invokerRootPane = SwingUtilities.getRootPane(invoker);
				if (invokerRootPane != null)
				{
					invokerRootPane.addFocusListener(rootPaneFocusListener);
					invokerRootPane.requestFocus(true);
					invokerRootPane.addKeyListener(menuKeyboardHelper);
					focusTraversalKeysEnabled = invokerRootPane
							.getFocusTraversalKeysEnabled();
					invokerRootPane.setFocusTraversalKeysEnabled(false);

					menuInputMap = getInputMap(popup, invokerRootPane);
					addUIInputMap(invokerRootPane, menuInputMap);
					addUIActionMap(invokerRootPane, menuActionMap);
				}
			}
			else if (( lastPathSelected.length != 0 ) && ( p.length == 0 )) // menu
				// hidden
				// --
				// return
				// focus
				// to
				// where
				// it
				// had
				// been
				// before
				// and uninstall menu keybindings
				removeItems();
			else if (popup != lastPopup) receivedKeyPressed = false;

			// Remember the last path selected
			lastPathSelected = p;
			lastPopup = popup;
		}

		public void keyPressed(KeyEvent ev)
		{
			receivedKeyPressed = true;
			MenuSelectionManager.defaultManager().processKeyEvent(ev);
		}

		public void keyReleased(KeyEvent ev)
		{
			if (receivedKeyPressed)
			{
				receivedKeyPressed = false;
				MenuSelectionManager.defaultManager().processKeyEvent(ev);
			}
		}

		public void keyTyped(KeyEvent ev)
		{
			if (receivedKeyPressed)
				MenuSelectionManager.defaultManager().processKeyEvent(ev);
		}
	}
}
